/*
 * MIT License
 * 
 * Copyright (c) 2017 wen.gu <454727014@qq.com>
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

/*****************************************************************************
 * Name: tiny_meta.c
 *
 * Purpose:  meta API implementation
 *
 * Developer:
 * wen.gu,2016-05-09
 *
 *****************************************************************************/

#include <string.h>

#include "general_error.h"
#include "general_macro.h"
#include "tiny_meta.h"
#include "tiny_list.h"
#include "vos_mem.h"

/***************************************************************************
 *
 * macro define
 *
 ***************************************************************************/ 
#define LOGE printf
#define LOGD printf
#define LOGW printf

#define isUseReservoir(size) ((size) <= sizeof(value_reservoir_t))
/***************************************************************************
 *
 * struct define
 *
 ***************************************************************************/

typedef GFLT value_reservoir_t;

/** enum for the data type of value */
typedef enum my_item_value_type_e
{
	MIV_TypeNone    = GFCC('n', 'o', 'n', 'e'),
	MIV_TypeString  = GFCC('s', 't', 'r', 'i'),
	MIV_TypeInt32   = GFCC('i','n','3','2'),
	MIV_TypeInt64   = GFCC('i','n','6','4'),
	MIV_TypeFloat   = GFCC('f','l','o','a'),
	MIV_TypePointer = GFCC('p','t','r','#'),
	MIV_TypeBoolean = GFCC('b', 'o', 'o', 'l'),
	MIV_TypeRect    = GFCC('r', 'e','c','t'),
	MIV_TypeRange   = GFCC('r', 'a','n','g'),   
}my_item_value_type_t;

/**data structure for range value */
typedef struct my_range_s
{
	GS32 start;
	GS32 end;
}my_range_t;

/** data structure for rect value */
typedef struct my_rect_s
{
	GS32 left;
	GS32 top;
	GS32 right;
	GS32 bottom;
}my_rect_t;

/**date structure for meta item */
typedef struct my_item_s
{
	tiny_list_node_t node;

	/** the key of key-value */
	GU32 key; 

	/**the value info of key-value */
	GU32 type;
	GU32 size;
	union 
	{
		GPHD ext_data;
		value_reservoir_t reservoir;
	} u;
}my_item_t;

/** data structure for meta API */
typedef struct my_meta_s
{
	tiny_list_t items; /**< item linked list */
}my_meta_t;

/***************************************************************************
 *
 * inner API define
 *
 ***************************************************************************/

static void tmFindItemByKey(my_meta_t* mm, GU32 key, tiny_list_node_t** prev, tiny_list_node_t** node)
{
	tiny_list_t* tl = &(mm->items);
	tiny_list_node_t* head = tfListFront(tl);
	tiny_list_node_t* prev_node = head;
	GCHECK(prev);
	GCHECK(node);

	while (head)
	{
		my_item_t* mi = (my_item_t*)head;

		if (mi->key == key)
		{
			*prev = prev_node;
			*node = head;
			break;
		}

		prev_node = head;
		head = head->next;
		
	}
}

static my_item_t *tmItemNew(GU32 type, const void *data, GU32 size)
{
	GU32 mallocSize = sizeof(my_item_t);
	my_item_t *item = NULL;

	if (data)
	{
		if (!isUseReservoir(size))
		{
			mallocSize += size;
		}

		item = (my_item_t *)memAlloc(mallocSize);

		if (item)
		{
			memset(item, 0, mallocSize);

			if (isUseReservoir(size))
			{
				memcpy(&(item->u.reservoir), data, size);
			}
			else
			{
				item->u.ext_data = (void *)(((GS08*)item) + sizeof(my_item_t));
				memcpy(item->u.ext_data, data, size);
			}

			item->type = type;
			item->size = size;			
		}
	}
	
	return item;	
}

static void tmItemGetData(my_item_t *item, GU32 *type, const void **data, GU32 *size)
{
	*type = item->type;
	if (!(isUseReservoir(item->size)))
	{
		*data = item->u.ext_data;
	}
	else
	{
		*data = &(item->u.reservoir);
	}

	*size = item->size;
}

/***************************************************************************
 *
 * API define
 *
 ***************************************************************************/
GS32 tfMetaCreate(GPHD* meta)
{
	GS32 ret = G_ErrorBadParameter;

	if (meta)
	{
		my_meta_t* mm = (my_meta_t*)memAlloc(sizeof(my_meta_t));

		if (mm)
		{
			memset(mm, 0, sizeof(*mm));
			tfListInitialize(&(mm->items), memFree);
			*meta = mm;
			ret = G_OK;
		}
		else
		{
			LOGE("alloc meta instance failed\n");
			ret = G_ErrorInsufficientResources;
		}
	}

    return ret;
}

void tfMetaDestroy(GPHD meta)
{
	if (meta)
	{
		my_meta_t* mm = (my_meta_t*)meta;

		tfListClear(&(mm->items));

		memFree(meta);
	}
}

void tfMetaClear(GPHD meta)
{
	if (meta)
	{
		my_meta_t* mm = (my_meta_t*)meta;

		tfListClear(&(mm->items));
	}
}

GBOL tfMetaRemove(GPHD meta, GU32 key)
{
	GBOL ret = GFALSE;
	if (meta)
	{
		my_meta_t* mm = (my_meta_t*)meta;
		tiny_list_node_t* prev = NULL;
		tiny_list_node_t* node = NULL;
		tmFindItemByKey(mm, key, &prev, &node);

		if (prev && node)
		{
			tfListRemoveFast(&(mm->items), prev, node);
			ret = GTRUE; /**todo, is need add a return for API tfListRemoveFast */
		}

	}
    return ret;
}

GBOL tfMetaSetData(GPHD meta, GU32 key, GU32 type, const void* data, GU32 size)
{
	GBOL ret = GFALSE;
	if (meta)
	{
		my_meta_t* mm = (my_meta_t*)meta;
		tiny_list_node_t* prev = NULL;
		tiny_list_node_t* node = NULL;
		tmFindItemByKey(mm, key, &prev, &node);

		if (node)
		{
			tfListRemoveFast(&(mm->items), prev, node);
		}

		my_item_t* mi = tmItemNew(type, data, size);

		if (mi)
		{
			tfListPushBack(&(mm->items), (tiny_list_node_t*)mi);
			ret = GTRUE;
		}
	}
	return ret;
}

GBOL tfMetaFindData(GPHD meta, GU32 key, GU32* type, const void** data, GU32* size)
{
	GBOL ret = GFALSE;
	if (meta)
	{
		my_meta_t* mm = (my_meta_t*)meta;
		tiny_list_node_t* prev = NULL;
		tiny_list_node_t* node = NULL;
		tmFindItemByKey(mm, key, &prev, &node);

		if (node)
		{
			my_item_t* mi = (my_item_t*)node;
			tmItemGetData(mi, type, data, size);
			ret = GTRUE;
		}
	}
	return ret;
}
//////////////////////////////////////////////////////////////////////////

GBOL tfMetaSetInt32(GPHD meta, GU32 key, GS32 value)
{
	return tfMetaSetData(meta, key, MIV_TypeInt32, &value, sizeof(value));
}

GBOL tfMetaSetInt64(GPHD meta, GU32 key, GS64 value)
{
    return tfMetaSetData(meta, key, MIV_TypeInt64, &value, sizeof(value));
}

GBOL tfMetaSetString(GPHD meta, GU32 key, const char* value)
{
    return tfMetaSetData(meta, key, MIV_TypeString, value, strlen(value) + 1);
}

GBOL tfMetaSetFloat(GPHD meta, GU32 key, float value)
{
    return tfMetaSetData(meta, key, MIV_TypeFloat, &value, sizeof(value));
}

GBOL tfMetaSetPointer(GPHD meta, GU32 key, const void* value)
{
    return tfMetaSetData(meta, key, MIV_TypePointer, &value, sizeof(value));
}

GBOL tfMetaSetBoolean(GPHD meta, GU32 key, GBOL value)
{
	return tfMetaSetData(meta, key, MIV_TypeBoolean, &value, sizeof(value));
}

GBOL tfMetaSetRect(GPHD meta, GU32 key,
                    GS32 left, GS32 top,
                    GS32 right, GS32 bottom)
{
	my_rect_t rect;
	rect.left = left;
	rect.top = top;
	rect.right = right;
	rect.bottom = bottom;
	return tfMetaSetData(meta, key, MIV_TypeRect, &rect, sizeof(rect));
}

GBOL tfMetaSetRange(GPHD meta, GU32 key, GS32 start, GS32 end)
{
	my_range_t range;
	range.start =start;
	range.end = end;
	return tfMetaSetData(meta, key, MIV_TypeRange, &range, sizeof(range));
}

//////////////////////////////////////////////////////////////////////////

GBOL tfMetaFindInt32(GPHD meta, GU32 key, GS32* value)
{
	GBOL ret = GFALSE;
	GU32 type = 0;
	const void *data;
	GU32 size = 0;

	if ((tfMetaFindData(meta, key, &type, &data, &size)) && (type == MIV_TypeInt32))
	{
		*value = *(GS32 *)data;
		ret = GTRUE;
	}
	
	return ret;
}

GBOL tfMetaFindInt64(GPHD meta, GU32 key, GS64* value)
{
	GBOL ret = GFALSE;
	GU32 type = 0;
	const void *data;
	GU32 size = 0;

	if ((tfMetaFindData(meta, key, &type, &data, &size)) && (type == MIV_TypeInt64))
	{
		*value = *(GS64 *)data;
		ret = GTRUE;
	}

	return ret;
}

GBOL tfMetaFindString(GPHD meta, GU32 key, const char** value)
{
	GBOL ret = GFALSE;
	GU32 type = 0;
	const void *data;
	GU32 size = 0;

	if ((tfMetaFindData(meta, key, &type, &data, &size)) && (type == MIV_TypeString))
	{
		*value = (const char*)data;
		ret = GTRUE;
	}

	return ret;
}

GBOL tfMetaFindFloat(GPHD meta, GU32 key, float* value)
{
	GBOL ret = GFALSE;
	GU32 type = 0;
	const void *data;
	GU32 size = 0;

	if ((tfMetaFindData(meta, key, &type, &data, &size)) && (type == MIV_TypeFloat))
	{
		*value = *((float*)data);
		ret = GTRUE;
	}

	return ret;
}

GBOL tfMetaFindPointer(GPHD meta, GU32 key, const void** value)
{
	GBOL ret = GFALSE;
	GU32 type = 0;
	const void *data;
	GU32 size = 0;

	if ((tfMetaFindData(meta, key, &type, &data, &size)) && (type == MIV_TypePointer))
	{
		*value = (const void*)data;
		ret = GTRUE;
	}

	return ret;
}

GBOL tfMetaFindBoolean(GPHD meta, GU32 key, GBOL* value)
{
	GBOL ret = GFALSE;
	GU32 type = 0;
	const void *data;
	GU32 size = 0;

	if ((tfMetaFindData(meta, key, &type, &data, &size)) && (type == MIV_TypeBoolean))
	{
		*value = *(GBOL*)data;
		ret = GTRUE;
	}

	return ret;
}


GBOL tfMetaFindRect(GPHD meta, GU32 key,
    GS32* left, GS32* top,
    GS32* right, GS32* bottom)
{
	GBOL ret = GFALSE;
	GU32 type = 0;
	const void *data;
	GU32 size = 0;

	if ((tfMetaFindData(meta, key, &type, &data, &size)) && (type == MIV_TypeRect))
	{
		my_rect_t* rect = (my_rect_t*)data;
		*left = rect->left;
		*top = rect->top;
		*right = rect->right;
		*bottom = rect->bottom;
		ret = GTRUE;
	}

	return ret;
}


GBOL tfMetaFindRange(GPHD meta, GU32 key, GS32* start, GS32* end)
{
	GBOL ret = GFALSE;
	GU32 type = 0;
	const void *data;
	GU32 size = 0;

	if ((tfMetaFindData(meta, key, &type, &data, &size)) && (type == MIV_TypeRange))
	{
		my_range_t* range = (my_range_t*)data;
		*start = range->start;
		*end   = range->end;
		ret = GTRUE;
	}

	return ret;
}
